import { LinkedListQueue } from "@/library/tool-kits/linked-list-queqe"

type JobType = () => Promise<any>

/**
 * @description parallel promise scheduler
 * 
 * @example
 * ```
 * const scheduler = new Scheduler(5)
 * const addTask = (time: number, order: () => void) => {
 *  scheduler.add(time, order)
 * }
 * addTask(1000, () => console.log(1))
 * addTask(300, () => console.log(2))
 * addTask(800, () => console.log(3))
 * addTask(2000, () => console.log(4))
 * scheduler.start()
 * ```
 */
export class Scheduler<T extends () => void> {
  private maxCount: number = 5

  private runCount: number = 0

  private queue: LinkedListQueue<JobType> = new LinkedListQueue()

  constructor()

  constructor(maxCount: number)

  constructor(maxCount?: number) {
    if (maxCount) {
      this.maxCount = maxCount
    }
  }

  public add(time: number, order: T) {
    const promiseCreator = () => {
      return new Promise<T>((resolve) => {
        setTimeout(() => {
          resolve(order)
        }, time)
      })
    }
    this.queue.push(promiseCreator)
  }

  public start() {
    for (let i = 0; i < this.maxCount; i++) {
      this.run()
    }
  }

  private run() {
    if (!this.queue.size || this.runCount >= this.maxCount) {
      return
    }
    this.runCount++
    (this.queue.dequeue()!)()
      .then((res: T) => {
        res()
      })
      .catch(err => console.log(err))
      .finally(() => {
        this.runCount--
        this.run()
      })
  }

  public getRuncount() {
    return this.runCount
  }

  public getPendingCount() {
    return this.queue.size
  }

  public getMaxCount() {
    return this.maxCount
  }

  public setMaxCount(count: number) {
    this.maxCount = count
  }

  public clear() {
    this.queue.clear()
  }
}
